Previous Book Contents Book Index Next

Inside Macintosh: 3D Graphics Programming With QuickDraw 3D /
Chapter 5 - Attribute Objects


Using Attribute Objects

This section describes the basic capabilities that QuickDraw 3D provides to create and configure attribute sets. It also shows how to read the attributes in an attribute set and, if necessary, change those attributes. In general, it's very simple to create, configure, and modify attribute sets.

This section also shows how to define a custom attribute type. To do so, you need to provide definitions of the data associated with that attribute type and an attribute metahandler to define a set of attribute-handling methods. See "Defining Custom Attribute Types," beginning on page 5-9 for complete details.

Creating and Configuring Attribute Sets

You create a new attribute set by calling the Q3AttributeSet_New function. You configure the attribute set by adding the desired attributes to the set, using the Q3AttributeSet_Add function. Finally, you attach the configured attribute set to an object by calling an appropriate QuickDraw 3D routine. For example, to attach an attribute set to a vertex of a triangle, you call the function Q3Triangle_SetVertexAttributeSet. Listing 5-1 illustrates how to set the three vertices of a triangle to a specific diffuse color.

Listing 5-1 Creating and configuring a vertex attribute set

TQ3Status MySetTriangleVerticesDiffuseColor 
               (TQ3GeometryObject triangle, TQ3ColorRGB color)
{
   TQ3AttributeSet         myAttrSet;  /*attribute set*/
   TQ3Status               myResult;   /*result code*/
   unsigned long           myIndex;    /*vertex index*/

   /*Create a new empty attribute set.*/
   myAttrSet = Q3AttributeSet_New();
   if (myAttrSet == NULL)
      return (kQ3Failure);

   /*Add the specified color attribute to the attribute set.*/
   myResult = Q3AttributeSet_Add
            (myAttrSet, kQ3AttributeTypeDiffuseColor, &color);
   if (myResult == kQ3Failure)
      return (kQ3Failure);

   /*Attach the attribute set to each triangle vertex.*/
   for (myIndex = 0; myIndex < 3; myIndex++) {
      myResult = Q3Triangle_SetVertexAttributeSet
                              (triangle, myIndex, myAttrSet);
      if (myResult == kQ3Failure)
         return (kQ3Failure);
   }

   return (kQ3Success);
}
You can assign any number of different attribute types to a single attribute set. The function defined in Listing 5-1 assigns only one attribute--a diffuse color--to the new attribute set.

If you want to change the value of a certain attribute in an attribute set, you can simply overwrite the data associated with that attribute by calling Q3AttributeSet_Add once again. You can remove an attribute from an attribute set by calling Q3AttributeSet_Clear. To remove all attributes from an attribute set, you can call Q3AttributeSet_Empty.

Iterating Through an Attribute Set

QuickDraw 3D provides the Q3AttributeSet_GetNextAttributeType function that you can use to iterate through the attributes in an attribute set. To get the first attribute in an attribute set, pass the constant kQ3AttributeTypeNone to Q3AttributeSet_GetNextAttributeType. You can retrieve any subsequent attributes by successively calling Q3AttributeSet_GetNextAttributeType, which returns kQ3AttributeTypeNone when you reach the end of the list of attributes. Listing 5-2 illustrates how to use Q3AttributeSet_GetNextAttributeType to determine the number of attributes in an attribute set.

Listing 5-2 Counting the attributes in an attribute set

unsigned long MyCountAttributesInSet (TQ3AttributeSet mySet)
{
   unsigned long           myCount;    /*attribute count*/
   TQ3AttributeType        myType;     /*attribute type*/
   TQ3Status               myResult;   /*result code*/

   for (myCount = 0,
        myType = kQ3AttributeTypeNone, 
        myResult = 
            Q3AttributeSet_GetNextAttributeType(mySet, &myType);
        myType != kQ3AttributeTypeNone;
        myResult = 
         Q3AttributeSet_GetNextAttributeType(mySet, &myType)) {
      myCount++;
   }
   
   return (myCount);
}
Notice that the Q3AttributeSet_GetNextAttributeType function returns a result code that indicates whether the call succeeded or failed. In general, the call fails only if the attribute set is invalid in some way.

Defining Custom Attribute Types

QuickDraw 3D allows you to define custom attribute types so that you can attach to a vertex (or face, or geometric object, or group, or view) types of data different from those associated with the basic attribute types defined by QuickDraw 3D. Once you have defined and registered your custom attribute type, you manipulate attributes of that type exactly as you manipulate the standard QuickDraw 3D attributes. For example, you add a custom attribute to an attribute set by calling Q3AttributeSet_Add, and you retrieve the data associated with a custom attribute by calling Q3AttributeSet_Get.

To define a custom attribute type, you first define the internal structure of the data associated with your custom attribute type. Then you must write an attribute metahandler to define a set of attribute-handling methods. QuickDraw 3D calls those methods at certain times to handle operations on attribute sets that contain your custom attribute. For example, when you call Q3Triangle_Write to write a triangle to a file, QuickDraw 3D might need to call your attribute's handler to write your custom attribute data to the file.

Suppose that you want to define a custom attribute that contains data about temperature over time. You might use the MyTemperatureData structure, defined like this:

typedef struct MyTemperatureData {
   unsigned long        startTime;     /*starting time*/
   unsigned long        nTemps;        /*no. temps in array*/
   float                *temperatures; /*array of temps*/
} MyTemperatureData;
Your attribute metahandler is an application-defined function that returns the addresses of the methods associated with the custom attribute type. A metahandler can define some or all of the methods indicated by these constants:

kQ3MethodTypeObjectReadData
kQ3MethodTypeObjectTraverse
kQ3MethodTypeObjectWrite
kQ3MethodTypeElementCopyAdd
kQ3MethodTypeElementDelete
kQ3MethodTypeElementCopyDuplicate
kQ3MethodTypeElementCopyGet
kQ3MethodTypeElementCopyReplace
kQ3MethodTypeAttributeCopyInherit
kQ3MethodTypeAttributeInherit
Listing 5-3 defines a simple attribute metahandler. See "Defining an Object Metahandler," beginning on page 3-13 for a more complete description of metahandlers.

Listing 5-3 Reporting custom attribute methods

TQ3FunctionPointer MyTemperatureDataMetaHandler (TQ3MethodType methodType)
{
   switch (methodType) {
      case kQ3MethodTypeElementDelete:
         return (TQ3FunctionPointer) MyTemperatureDataDispose;
      case kQ3MethodTypeElementCopyReplace:
         return (TQ3FunctionPointer) MyTemperatureDataCopyReplace;
      case kQ3MethodTypeAttributeCopyInherit:
         return (TQ3FunctionPointer) kQ3True;
      case kQ3MethodTypeAttributeInherit:
         return (TQ3FunctionPointer) kQ3True;
      default:
         return (NULL);
   }
}
As you can see, the MyTemperatureDataMetaHandler metahandler simply returns the appropriate function address, or NULL if the metahandler does not implement a particular method type. All the method types listed above are optional. (In fact, you don't need to specify a metahandler at all if you want QuickDraw 3D to use its default methods to handle your custom attribute type.)

The metahandler defined in Listing 5-3 installs the MyTemperatureDataDispose function as the custom attribute's dispose method, which QuickDraw 3D calls whenever you clear your custom attribute or replace an existing custom attribute. A dispose method is passed a pointer to the data associated with an attribute. Your dispose method should deallocate any storage you allocated yourself. Listing 5-4 shows a simple dispose method.

Listing 5-4 Disposing of a custom attribute's data

TQ3Status MyTemperatureDataDispose (MyTemperatureData *tmpData)
{
   if (tData->temperatures != NULL) {
      free(tmpData->temperatures);
      tData->temperatures = NULL;
   }
   return kQ3Success;
}
If you do not define a dispose method, QuickDraw 3D automatically disposes of the block of data allocated when a custom attribute was added to an attribute set. If the data associated with a custom attribute is always of a fixed size and does not contain any pointers to other data that needs to be disposed of, you do not need to define a dispose or copy method.

The metahandler defined in Listing 5-3 installs the MyTemperatureDataCopyReplace function as the custom attribute's copy method. A copy method is passed two pointers, specifying the source and target addresses of the data to copy. Listing 5-5 shows a simple copy method.

Listing 5-5 Copying a custom attribute's data

TQ3Status MyTemperatureDataCopyReplace 
         (const MyTemperatureData *src, MyTemperatureData *dst)
{
   float                   *temp;

   if (dst->nTemps != src->nTemps) {
      temp = realloc(dst->temperatures, nTemps * sizeof(float));
      if (temp == NULL)
         return (kQ3Failure);
   }
   dst->startTime = src->startTime;
   dst->nTemps    = src->nTemps;
   dst->temperatures = temp;

   memcpy(temp, dst->temperatures, dst->nTemps * sizeof(float));

   return (kQ3Success);
}
If you do not define a copy method, QuickDraw 3D automatically copies the block of data using a default memory copy method.

The inherit method simply requests a Boolean value that indicates whether you want your custom attribute to be inherited down the class hierarchy. You should return kQ3True if you want your attribute to be inherited or kQ3False if not.

Before you can use a custom attribute type, you need to register your attribute metahandler with QuickDraw 3D by calling the Q3AttributeClass_Register function. You might execute the MyStartUpQuickDraw3D function defined in Listing 5-6 at application startup time.

Listing 5-6 Initializing QuickDraw 3D and registering a custom attribute type

TQ3AttributeType           gAttributeType_Temperature;

void MyStartUpQuickDraw3D (void)
{
   TQ3ObjectClass          myAttrib;

   if (Q3Initialize() == kQ3Failure)/*initialize QuickDraw 3D*/
      MyFailRoutine();
                              /*register attribute type*/
   myAttrib = Q3AttributeClass_Register(
                           gAttributeTypeTemperature, 
                           "MyCompany:SurfWorks:Temperature", 
                           sizeof(MyTemperatureData), 
                           MyTemperatureData_MetaHandler);

   if (myAttrib == kQ3ObjectTypeInvalid)
      MyFailRoutine();
}

Previous Book Contents Book Index Next

© Apple Computer, Inc.
11 JUL 1996